package global

import (
	"daily-blog/pkg"
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/utils"
	"time"
)

type SQL struct {
	Timestamp    string `json:"timestamp"`     // 时间，格式：2006-01-02 15:04:05
	Stack        string `json:"stack"`         // 文件地址和行号
	SQL          string `json:"sql"`           // SQL 语句
	Rows         int64  `json:"rows_affected"` // 影响行数
	CostMSeconds int64  `json:"cost_mseconds"` // 执行时长(单位毫秒)
}
type TracePlugin struct{}

func (op *TracePlugin) Name() string {
	return "tracePlugin"
}

func (op *TracePlugin) Initialize(db *gorm.DB) (err error) {
	// 开始前
	_ = db.Callback().Create().Before("gorm:before_create").Register("callBackBeforeName", before)
	_ = db.Callback().Query().Before("gorm:query").Register("callBackBeforeName", before)
	_ = db.Callback().Delete().Before("gorm:before_delete").Register("callBackBeforeName", before)
	_ = db.Callback().Update().Before("gorm:setup_reflect_value").Register("", before)
	_ = db.Callback().Row().Before("gorm:row").Register("callBackBeforeName", before)
	_ = db.Callback().Raw().Before("gorm:raw").Register("callBackBeforeName", before)

	// 结束后
	_ = db.Callback().Create().After("gorm:after_create").Register("callBackAfterName", after)
	_ = db.Callback().Query().After("gorm:after_query").Register("callBackAfterName", after)
	_ = db.Callback().Delete().After("gorm:after_delete").Register("callBackAfterName", after)
	_ = db.Callback().Update().After("gorm:after_update").Register("callBackAfterName", after)
	_ = db.Callback().Row().After("gorm:row").Register("callBackAfterName", after)
	_ = db.Callback().Raw().After("gorm:raw").Register("callBackAfterName", after)
	return
}

var _ gorm.Plugin = &TracePlugin{}

func before(db *gorm.DB) {
	db.InstanceSet("startTime", time.Now())
	return
}

func after(db *gorm.DB) {

	_ts, isExist := db.InstanceGet("startTime")
	if !isExist {
		return
	}

	ts, ok := _ts.(time.Time)
	if !ok {
		return
	}

	sql := db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)
	// SQL info
	sqlInfo := new(SQL)
	sqlInfo.CostMSeconds = time.Since(ts).Milliseconds()
	sqlInfo.Timestamp = ts.Format("2006-01-02 15:04:05")
	sqlInfo.SQL = sql
	sqlInfo.Stack = utils.FileWithLineNum()
	sqlInfo.Rows = db.Statement.RowsAffected
	// 发送邮件
	if sqlInfo.CostMSeconds > 200 {
		email := GVA_CONFIG.Email
		body := fmt.Sprintf("SQL_Info:%+v", sqlInfo)
		pkg.SendToMail(email.From, email.Nickname, email.Secret, email.Host+":"+email.Port, "Daily-Blog", body, "html", []string{email.To})
	}
	return
}
